<template>
  <n-card
    title=" "
    header-class="p-4"
    content-class="pl-4 pr-4"
    :bordered="false"
  >
    <template #header-extra>
      <n-tooltip trigger="hover">
        <template #trigger>
          <n-icon
            size="18"
            class="mr-1 cursor-pointer hover:text-blue-600"
            @click="handleAdd"
          >
            <PlusSquareOutlined />
          </n-icon>
        </template>
        <span>新增</span>
      </n-tooltip>
      <n-tooltip trigger="hover">
        <template #trigger>
          <n-icon
            size="18"
            class="mr-1 cursor-pointer hover:text-blue-600"
            @click="handleDelete"
          >
            <DeleteOutlined />
          </n-icon>
        </template>
        <span>删除</span>
      </n-tooltip>
      <n-tooltip trigger="hover">
        <template #trigger>
          <n-icon
            size="18"
            class="cursor-pointer hover:text-blue-600"
            @click="handleRefresh"
          >
            <ReloadOutlined />
          </n-icon>
        </template>
        <span>刷新</span>
      </n-tooltip>
    </template>
    <div id="data-content">
      <div
        v-if="$slots.toolbar"
        class="rounded-lg bg-gray-50 flex flex-row gap-2 items-center justify-end p-5 mb-5"
      >
        <slot name="toolbar"></slot>
      </div>
      <n-data-table
        remote
        v-model:checked-row-keys="selectRowKeys"
        :loading="loading"
        :bordered="false"
        :single-line="false"
        :columns="tableColumns"
        :data="tableData"
        :pagination="pagination"
        :row-key="(row: any) => row[idField]"
      />
      <div v-if="$slots.addModal">
        <n-modal
          title="新增"
          preset="dialog"
          v-model:show="showAddModal"
          :closable="false"
          :close-on-esc="false"
          :mask-closable="false"
          :style="{ width: '800px' }"
        >
          <slot name="addModal"></slot>
          <template #action>
            <n-space>
              <n-button
                type="primary"
                :loading="saveLoading"
                @click="handleSave"
                >保存</n-button
              >
              <n-button @click="closeModal">取消</n-button>
            </n-space>
          </template>
        </n-modal>
      </div>
      <div v-if="$slots.editModal">
        <n-modal
          title="编辑"
          v-model:show="showEditModal"
          preset="dialog"
          :closable="false"
          :close-on-esc="false"
          :mask-closable="false"
          :style="{ width: '800px' }"
        >
          <slot name="editModal"></slot>
          <template #action>
            <n-space>
              <n-button
                type="primary"
                :loading="saveLoading"
                @click="handleSave"
                >保存</n-button
              >
              <n-button @click="closeModal">取消</n-button>
            </n-space>
          </template>
        </n-modal>
      </div>
    </div>
  </n-card>
</template>

<script lang="ts" setup>
import {
  type DataTableColumnActionDefine,
  type DataTableColumnDefine,
  type DataTableProps,
  type DataTableMethods,
} from "./typing";

import {
  NButton,
  NCard,
  NDropdown,
  NIcon,
  useDialog,
  useMessage,
} from "naive-ui";
import { computed, h, onMounted, ref, toRaw } from "vue";
import {
  MoreOutlined,
  ReloadOutlined,
  PlusSquareOutlined,
  DeleteOutlined,
} from "@vicons/antd";

const $dialog = useDialog();
const $message = useMessage();
const loading = ref(false);
const selectRowKeys = ref<string[]>([]);
const tableData = ref<any[]>([]);
const editRow = ref<any>(null);
const showAddModal = ref(false);
const showEditModal = ref(false);
const saveLoading = ref(false);

const pagination = ref({
  page: 1,
  itemCount: 0,
  pageSize: 10,
  pageSizes: [10, 20, 30, 50],
  showSizePicker: true,
  onChange: async (page: number) => {
    pagination.value.page = page;
    await refreshData();
  },
  onUpdatePageSize: async (pageSize: number) => {
    pagination.value.pageSize = pageSize;
    await refreshData();
  },
});

const props = withDefaults(defineProps<DataTableProps>(), { idField: "id" });

const renderAction = (actions?: DataTableColumnActionDefine[]) => {
  const renderOptions = (row: any) => {
    const options = [] as any[];
    if (actions) {
      actions.forEach((action) => {
        if (action.key !== "edit" && action.key !== "delete") {
          options.push({
            label: action.name,
            key: action.key,
            props: { onClick: () => action.action(row) },
          } as never);
        }
      });
    }
    return options;
  };

  return {
    title: "操作",
    key: "actions",
    width: 100,
    fixed: "left",
    render: (row: any) =>
      h(
        NDropdown,
        {
          options: [
            {
              label: "编辑",
              key: "edit",
              props: { onClick: () => handleEdit(row) },
            },
            {
              label: "删除",
              key: "delete",
              props: { onClick: () => handleDeleteItem(row) },
            },
            ...renderOptions(row),
          ],
          placement: "bottom-start",
        },
        {
          default: () =>
            h(
              NButton,
              { size: "small", type: "primary" },
              { default: () => ["操作", h(NIcon, () => h(MoreOutlined))] },
            ),
        },
      ),
  };
};

const tableColumns = computed(() => {
  const actions = props.columns.reduce<DataTableColumnDefine | undefined>(
    (prev, current) => {
      return current.key === "action" ? current : prev;
    },
    undefined,
  )?.action;
  const cols = props.columns
    .filter((x: DataTableColumnDefine) => x.key != "action")
    .map((x: DataTableColumnDefine) => ({
      title: x.title,
      key: x.key,
      width: x.width,
      render: x.render,
      ellipsis: x.ellipsis ? { tooltip: true } : false,
    }));

  return [{ type: "selection", fixed: "left" }, ...cols, renderAction(actions)];
});

const handleRefresh = async () => {
  await refreshData();
};

const handleDeleteItem = (row: any) => {
  deleteRows([row]);
};

const handleDelete = () => {
  const rows = getSelectedRows();
  deleteRows(rows);
};

const deleteRows = (rows: any[]) => {
  if (rows && rows.length > 0) {
    $dialog.warning({
      title: "警告",
      content: "删除数据不可恢复, 确认删除吗?",
      positiveText: "确定",
      negativeText: "取消",
      onPositiveClick: async () => {
        if (props.onDelete) {
          try {
            const ret = await props.onDelete(rows);
            if (ret) {
              refreshData();
              $message.success("删除成功");
            } else {
              $message.error("删除失败");
            }
          } catch (e) {
            console.error(e);
            $message.error("删除异常,详情F12请查看控制台日志");
          }
        }
      },
    });
  }
};

const handleAdd = () => {
  if (props.onOpenModal) {
    props.onOpenModal();
  }
  showEditModal.value = false;
  showAddModal.value = true;
};

const handleEdit = async (row: any) => {
  if (props.onOpenModal) {
    props.onOpenModal(row);
  }
  if (props.onGet) {
    row = await props.onGet(row[props.idField]);
  }
  editRow.value = row;
  Object.assign(props.item, row);
  showAddModal.value = false;
  showEditModal.value = true;
};

const handleSave = async () => {
  if (props.onSave) {
    try {
      saveLoading.value = true;
      const res = showAddModal.value
        ? await props.onSave()
        : await props.onSave(editRow.value[props.idField]);
      if (res) {
        closeModal();
        await refreshData();
      }
    } catch (e) {
      console.error(e);
      $message.error("操作失败, 详情F12请查看控制台日志");
    } finally {
      saveLoading.value = false;
    }
  }
};

const closeModal = () => {
  if (props.onCloseModal) {
    props.onCloseModal();
  }
  Object.keys(props.item).forEach((key) => (props.item[key] = undefined));
  editRow.value = null;
  showAddModal.value = false;
  showEditModal.value = false;
};

const getSelectedRows = <T,>() => {
  if (!selectRowKeys.value) {
    return [];
  }
  const res = tableData.value
    .filter((x: any) => selectRowKeys.value.includes(x.id))
    .map((x: any) => toRaw(x));
  return res as T[];
};

const getSelectedRow = <T,>() => {
  return getSelectedRows<T>()[0] as T;
};

const getData = <T,>() => {
  return toRaw(tableData.value) as T[];
};

const refreshData = async () => {
  if (loading.value) return;
  loading.value = true;
  try {
    if (props.onLoad) {
      var { data, total } = await props.onLoad(
        pagination.value.page,
        pagination.value.pageSize,
      );
      tableData.value = data;
      pagination.value.itemCount = total;
    }
  } catch (error) {
    console.error(error);
    $message.error("获取数据失败, 详情F12请查看控制台日志");
  } finally {
    loading.value = false;
  }
};

onMounted(async () => await handleRefresh());
defineExpose<DataTableMethods>({
  getData,
  getSelectedRow,
  getSelectedRows,
  closeModal,
  refreshData,
  resetPage: () => {
    pagination.value.page = 1;
  },
});
</script>

<style lang="less" scoped></style>
